/**
 *  Copyright (C) 2023 - Fake Inc.
 *
 *  All Rights Reserved.

*/

#ifndef INNO_LIDAR_LOG_CPP_H_
#define INNO_LIDAR_LOG_CPP_H_
#include <cstdint>
#include <sstream>
#include <string>
#include <utility>

/*
NOTE: The log macros below should be considered for exemption from MISRA inspection constraints as needed.
Otherwise, printing logs with file, function, and line information might be less convenient.

#ifndef INNO_LOG_MACROS_H
#define INNO_LOG_MACROS_H

#define INNO_LOG_DEBUG(...) \
  Fake::inno_log(Fake::InnoLogLevel::INNOLOG_LEVEL_DEBUG, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

#define INNO_LOG_INFO(...) \
  Fake::inno_log(Fake::InnoLogLevel::INNOLOG_LEVEL_INFO, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

#define INNO_LOG_WARN(...) \
  Fake::inno_log(Fake::InnoLogLevel::INNOLOG_LEVEL_WARN, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

#define INNO_LOG_ERROR(...) \
  Fake::inno_log(Fake::InnoLogLevel::INNOLOG_LEVEL_ERROR, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#endif  // INNO_LOG_MACROS_H
*/
namespace Fake {

enum class InnoLogLevel : int32_t {
  INNOLOG_LEVEL_TRACE = 0,
  INNOLOG_LEVEL_DEBUG = 1,
  INNOLOG_LEVEL_INFO = 2,
  INNOLOG_LEVEL_WARN = 3,
  INNOLOG_LEVEL_ERROR = 4,
  INNOLOG_LEVEL_CRITICAL = 5,
  INNOLOG_LEVEL_OFF = 6
};

class LogHelper {
 public:
  LogHelper() = delete;

  explicit LogHelper(InnoLogLevel src_lev, const std::string &src_file, const std::string &src_func,
                     uint32_t src_lineno) noexcept
      : log_lev_(src_lev), log_file_(src_file), log_func_(src_func), log_lineno_(src_lineno) {
  }

  inline LogHelper &operator<<(std::ostream &(*const logger)(std::ostream &)) noexcept {
    ss_ << logger;
    return *this;
  }

  template <typename ARGS_TYPE>
  inline LogHelper &operator<<(const ARGS_TYPE &log_value) noexcept {
    if (ss_.str().length() > 0) {
      ss_ << " ";
    }
    ss_ << log_value;
    return *this;
  }

  LogHelper(const LogHelper &) = default;
  LogHelper &operator=(const LogHelper &) = default;
  LogHelper(LogHelper &&) noexcept = default;
  LogHelper &operator=(LogHelper &&) noexcept = default;

  ~LogHelper() noexcept;

 private:
  InnoLogLevel log_lev_;
  std::stringstream ss_;
  std::string log_file_;
  std::string log_func_;
  uint32_t log_lineno_;
};

void init_logger();
void inno_iov(const std::string &src_file, const std::string &src_func, int32_t src_line, InnoLogLevel src_lev,
              const std::string &log_msg);

static constexpr uint32_t kLogBufferSize{1024};

template <typename... ARGS_TYPE>
void inno_log(InnoLogLevel src_lev, const std::string &src_file, const std::string &src_func, int32_t src_line,
              const std::string &fmt_str, ARGS_TYPE &&... fmt_args) {
  std::string tmp_buf(kLogBufferSize, '\0');
  int32_t sp_res{0};

  sp_res = std::snprintf(&tmp_buf[0], tmp_buf.size(), fmt_str.c_str(), std::forward<ARGS_TYPE>(fmt_args)...);
  if (sp_res >= 0) {
    inno_iov(src_file, src_func, src_line, src_lev, tmp_buf);
  }
}

extern "C" {
typedef void (*InnoLogCallback)(const int8_t *const src_file, const int8_t *src_func, int32_t src_line, int32_t src_lev,
                                const int8_t *log_msg);

void register_callback(InnoLogCallback log_cb);
void start_c_module();
}

}  // namespace Fake

#endif
